using System;
using System.Reflection;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;

using SBPweb.Data;

using SBPweb.Common.Caching;
using SBPweb.Common.Session;

using SBPweb.PROvibe.Resources.Warehouse.Module;

using SBPweb.PROvibe.Business.Logic;

namespace SBPweb.PROvibe.Business.Warehouse
{

	public class ModuleFactory
	{

		#region Private static string constants

		private static string CACHEKEY_MODULES = "Warehouse.Modules";
		private static string CACHEKEY_MODULES_INDEX_ID = "Warehouse.Modules.Index.Id";
		private static string CACHEKEY_MODULES_INDEX_NAME = "Warehouse.Modules.Index.Name";

		private static string SESSIONKEY_MODULES = "_Repository.Modules";

		#endregion

		#region Constructor logic

		private ModuleFactory()
		{
		}

		#endregion

        #region Static properties

        internal static ModuleRepository DefaultRepository
        {
            get
            {
                ModuleRepository ret = SessionHandler.Get<ModuleRepository>(SESSIONKEY_MODULES);

                if (ret == null)
                {
                    ret = new ModuleRepository(ConfigurationManager.ConnectionStrings.Warehouse);
                    SessionHandler.Put<ModuleRepository>(SESSIONKEY_MODULES, ret);
                }

                return ret;
            }
        }

        #endregion

		#region Repository handler methods

		#region Create

		public static Business.Entities.Module CreateModule(string name, string filePath)
		{
            Resources.Entities.Module module = new SBPweb.PROvibe.Resources.Entities.Module();
            Resources.Entities.Stream stream = new SBPweb.PROvibe.Resources.Entities.Stream();

            Transaction tran = TransactionFactory.CreateTransaction();
            
            try
            {
                tran.BeginTransaction(false);

                tran.RegisterSqlRepository(ModuleFactory.DefaultRepository.Wrapper);
                tran.RegisterSqlRepository(StreamFactory.DefaultRepository.Wrapper);

                #region Creating Stream entity

                #region Assembly loading

                FileStream fS = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                int fileSize = (int)fS.Length;
                byte[] bytes = (byte[])Array.CreateInstance(typeof(byte), fileSize);

                fS.Position = 0;
                fS.Read(bytes, 0, fileSize);
                fS.Close();

                Assembly asm = Assembly.ReflectionOnlyLoadFrom(filePath);

                #endregion

                stream.RawData = bytes;

                StreamFactory.DefaultRepository.Add(stream);

                tran.RegisterSqlEntity(stream);

                #endregion

                #region Creation Module entity

                module.Name = name;
                module.AssemblyName = asm.GetName();
                FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location);
                module.AssemblyVersion = module.AssemblyName.Version;
                module.AssemblyFileVersion = new System.Version(fvi.FileVersion);

                module.AssemblyStreamId = stream.Id;

                DefaultRepository.Add(module);
                
                tran.RegisterSqlEntity(module);

                #endregion

                tran.Commit();

                return ConvertResourcesEntity(module);
            }
            catch (Exception ex)
            {
                tran.Rollback();
                throw ex;
            }
            finally
            {
                tran.Terminate();
            }
		}

		#endregion

		#region Read

		public static List<Entities.Module> ReadModules()
		{
			bool isCached = ConfigurationManager.CacheExpirations.IsCachingUsed;

			if (isCached && CacheHandler.GetAccessLogClone().ContainsKey(CACHEKEY_MODULES))
			{
				return CacheHandler.Retrieve<List<Entities.Module>>(CACHEKEY_MODULES);
			}

			List<Entities.Module> modules = ConvertResourcesEntityList(DefaultRepository.GetModules());

			if (isCached)
			{
				CacheHandler.Store<List<Entities.Module>>(CACHEKEY_MODULES, modules, ConfigurationManager.CacheExpirations.Warehouse.Modules);

				Dictionary<int, Entities.Module> indexId = new Dictionary<int, Entities.Module>();
				Dictionary<string, Entities.Module> indexName = new Dictionary<string, Entities.Module>();

				foreach (Entities.Module module in modules)
				{
					indexId.Add(module.Id, module);
					indexName.Add(module.Name, module);
				}

				CacheHandler.Store<Dictionary<int, Entities.Module>>(CACHEKEY_MODULES_INDEX_ID, indexId, ConfigurationManager.CacheExpirations.Warehouse.Modules);
				CacheHandler.Store<Dictionary<string, Entities.Module>>(CACHEKEY_MODULES_INDEX_NAME, indexName, ConfigurationManager.CacheExpirations.Warehouse.Modules);
			}

			return modules;
		}

		public static Entities.Module ReadModuleById(int moduleId)
		{
			bool isCached = ConfigurationManager.CacheExpirations.IsCachingUsed;
			Dictionary<int, Entities.Module> index = null;

			if (isCached && CacheHandler.GetAccessLogClone().ContainsKey(CACHEKEY_MODULES_INDEX_ID))
			{
				index = CacheHandler.Retrieve<Dictionary<int, Entities.Module>>(CACHEKEY_MODULES_INDEX_ID);
				if (index.ContainsKey(moduleId))
				{
					return index[moduleId];
				}
			}

			Resources.Entities.Module resource = DefaultRepository.GetModuleById(moduleId);
            Entities.Module module = null;
            if (resource != null)
            {
                module = ConvertResourcesEntity(resource);
            }

			if (index != null)
			{
				index.Add(moduleId, module);
			}

			return module;
		}

		public static Entities.Module ReadModuleByName(string moduleName)
		{
			bool isCached = ConfigurationManager.CacheExpirations.IsCachingUsed;
			Dictionary<string, Entities.Module> index = null;

			if (isCached && CacheHandler.GetAccessLogClone().ContainsKey(CACHEKEY_MODULES_INDEX_NAME))
			{
				index = CacheHandler.Retrieve<Dictionary<string, Entities.Module>>(CACHEKEY_MODULES_INDEX_NAME);
				if (index.ContainsKey(moduleName))
				{
					return index[moduleName];
				}
			}

			Resources.Entities.Module resource = DefaultRepository.GetModuleByName(moduleName);
            Entities.Module module = null;
            if (resource != null)
            {
                module = ConvertResourcesEntity(resource);
            }

			if (index != null)
			{
				index.Add(moduleName, module);
			}

			return module;
		}

		#endregion

		#region Update

		public static void UpdateModule(Entities.Module module)
		{
			DefaultRepository.Update(module);
		}

		#endregion

		#region Delete

		public static void DeleteModule(int moduleId)
		{
			DefaultRepository.Remove(moduleId);
		}

		public static void DeleteModule(Entities.Module module)
		{
			DeleteModule(module.Id);
		}

		#endregion

		#endregion

		#region Internal converter methods

		internal static List<Entities.Module> ConvertResourcesEntityList(List<Resources.Entities.Module> list)
		{
			return list.ConvertAll<Entities.Module>(new Converter<Resources.Entities.Module, Entities.Module>(ConvertResourcesEntity));
		}

		internal static Entities.Module ConvertResourcesEntity(Resources.Entities.Module module)
		{
			return new Entities.Module(module);
		}

		#endregion


	}

}
